# -*- coding: utf-8 -*-
"""
Created on Wed Mar 31 14:42:03 2021

@author: Hu Yue
@email: hhhuyue@gmail.com

Note:
"""
import pandas as pd
import numpy as np
import os
import warnings
warnings.filterwarnings("ignore")

import traceback
import logging
logging.basicConfig(filename='log.log')

# 读取期权价格数据和股票价格数据
option_price = pd.read_feather("./data/optionprice.feather")
stock_price = pd.read_feather("./data/stockprice.feather")

#%%
# calculate the time2mat
option_price['Date'] = pd.to_datetime(option_price.Date,format="%Y-%m-%d")
option_price.loc[:,'Expiration'] = pd.to_datetime(option_price.Expiration,format="%Y-%m-%d")
option_price.loc[:,'time2mat'] = ((option_price['Expiration'] - option_price['Date'])/pd.Timedelta(1,'D')).fillna(0).astype(int)

# ignore the options whose time2mat is less than 7
# =============================================================================
# option_price_ = option_price_[option_price_['time2mat']>=7]
# # delete the options whose delta equals to  -99.99
# option_price_ = option_price_[(np.abs(option_price_['Delta'])<=0.95) & (np.abs(option_price_['Delta'])>=0.05)]
# # delete the options whose volume is equals to 0
# option_price_ = option_price_[option_price_['Volume']>0]
# 
# =============================================================================
# groupby  key variables: Date,time2mat,CallPut

 #%%
import json
class MyEncoder(json.JSONEncoder):
   def default(self,obj):
       if isinstance(obj,np.integer):
           return int(obj)
       elif isinstance(obj, np.floating):
           return float(obj)
       elif isinstance(obj,np.ndarray):
           return obj.tolist()
       else:
           return super(MyEncoder,self).default(obj)
def get_stock_price(SecurityID,start_date,end_date):
    """
    get the price of the stock in a period of time.
    """
    
    specific_stock_price = stock_price[['Date','SecurityID','Price']][stock_price['SecurityID'] == SecurityID]
    specific_stock_price.loc[:,'Date'] = pd.to_datetime(specific_stock_price.Date,format="%Y-%m-%d")
    specific_stock_price = specific_stock_price[(specific_stock_price['Date']<=end_date)&(specific_stock_price['Date']>=start_date)]
    specific_stock_price.set_index(['Date'],inplace=True,drop=False)
    return specific_stock_price

def get_option_info(OptionID,start_date,end_date):

    specific_option = option_price[(option_price['Date'] >= start_date) & (option_price['OptionID'] == OptionID) & (option_price['Date'] <= end_date) ]
    specific_option.loc[:,'spread'] = specific_option['BestOffer'] -specific_option['BestBid']
    specific_option['ImpliedVolatility'][specific_option['ImpliedVolatility'] <-99] =np.nan
   
    specific_option.loc[:,'midprice']  = (specific_option['BestOffer']+specific_option['BestBid'])/2
    specific_option.set_index(['Date'],inplace=True,drop=False)
    specific_option['Strike'] = specific_option['Strike']/1000
    new_end_date = min(specific_option.Date.max(),end_date)
    
    return specific_option,new_end_date


def get_hedged_retDT1(SecurityID,start_date,end_date,OptionID):
    
    stock_price = get_stock_price(SecurityID,start_date,end_date)
    #print("stock_price",SecurityID)
    option_info,new_end_date = get_option_info(OptionID,start_date,end_date)
    #print("option_info",OptionID)
    
    if len(stock_price) ==0 or len(option_info) ==0:
        logging.error("期权{}或证券{}没有相应的数据啦！".format(OptionID,SecurityID))
        return {}
    
    option_info.loc[:,'ImpliedVolatility'] = option_info.loc[:,'ImpliedVolatility'].fillna(method='ffill')
    
    start_price = stock_price.loc[start_date,'Price']
    
    sigma =option_info.loc[start_date,'ImpliedVolatility']
    delta = option_info.loc[start_date,'Delta']
    time2mat = option_info.loc[start_date,'time2mat']
    cptype = option_info.loc[start_date,'CallPut']
    K = option_info.loc[start_date,'Strike']
    start_cost = option_info.loc[start_date,'midprice'] 
    
    try:
        end_price = stock_price.loc[new_end_date,'Price']
    except:
        s = traceback.format_exc()
        logging.error("证券代码为({})的股票没有足够的数据！ ".format(SecurityID))
        logging.error(s)
        return {}
      
    
    endcost1 =  option_info.loc[new_end_date,'midprice']
    
    rate = 0.04/365/100
        
    # 计算每天的对冲成本和总的对冲成本
    hedge_info = stock_price.join(option_info.drop(['Date','SecurityID'],axis=1))
    
    if len(hedge_info) ==0:
         return {}
         
    hedge_info.sort_index(inplace=True)
    #hedge_info['Delta'][hedge_info['time2mat']==0] = (cptype=="Call")*1
    hedge_info.loc[:,'sign'] = (hedge_info['CallPut']=='C')*1
    hedge_info.loc[:,'sign_up'] = (hedge_info['Price']>hedge_info['Strike'])*1
    hedge_info.loc[:,'sign_down'] = (hedge_info['Price']<hedge_info['Strike'])*1
    
    hedge_info['Delta'][hedge_info['Delta'] <-99] = hedge_info['sign']*(1-hedge_info['sign_down']) + (1-hedge_info['sign'])*(hedge_info['sign_up']-1)
    
# =============================================================================
#     hedge_info['shares_bought'] = hedge_info['Delta'] - hedge_info['Delta'].shift(1).fillna(0)
#     hedge_info['PnL'] =  hedge_info['shares_bought'] *  hedge_info['Price']
# =============================================================================
    hedge_info['diff_price'] =hedge_info['Price'].shift(-1)-hedge_info['Price']
    hedge_info['PnL']= -(hedge_info['diff_price']*hedge_info['Delta'])
    hedge_info['days_diff'] = hedge_info['time2mat'].shift(-1) -hedge_info['time2mat']
    hedge_info['interest'] = (-start_cost + hedge_info['Delta']*hedge_info['Price']) * rate*hedge_info['days_diff']
    
    # 记录当天的损益
    hedge_info['Date'] = hedge_info['Date'].apply(lambda x: x.strftime("%Y-%m-%d"))
    hedge_info['stock_gain'] = -(hedge_info['Price'].shift(-1) - hedge_info['Price'])*hedge_info['Delta']
    hedge_info['option_gain'] = hedge_info['midprice'].shift(-1) - hedge_info['midprice']
    hedge_info['total_gain'] = hedge_info['stock_gain'] + hedge_info['option_gain']
    hedge_info = hedge_info.round({"stock_gain":5,"option_gain":5,"total_gain":5})
    hedgeret = hedge_info['PnL'].sum()
    interest2 = hedge_info['interest'].sum()
    
    daily_pnl = np.array(hedge_info[['Date','stock_gain','option_gain','total_gain']]).tolist()
    
    if cptype == "Call":

        endcost2 = max(end_price-K,0)
    else:

        endcost2 = max(K-end_price,0)
           
    mat = ((option_info['time2mat'][option_info['Date']==start_date]).values[0]>0)*1
    endcost = mat*endcost2 +(1-mat)*endcost1
    
    PnL = start_cost - endcost - hedgeret - 0.35*option_info.loc[start_date,'spread'] -interest2 
    ret ={
        "start_cost":start_cost,
        "startspred":option_info.loc[start_date,'spread'],
        "startvega":option_info.loc[start_date,'Vega'],
        "startprice":start_price,
        "endcost1":endcost1,
        "endcost2":endcost2,
        "K":K,
        "endprice":end_price,
        "hedgeret":hedgeret,
        "mat":mat,
        "endspred":option_info.loc[new_end_date,'spread'],
        "SecurityID":SecurityID,
        "lastday": option_info.loc[start_date,'Expiration'].strftime("%Y-%m-%d"),
        "time2mat":time2mat,
        "startdate":start_date.strftime("%Y-%m-%d"),
        "end_date":end_date.strftime("%Y-%m-%d"),
        "OptionID":OptionID,
        "VOL":option_info.loc[start_date,'Volume'],
        "IV":sigma,
        "Gamma":option_info.loc[start_date,'Gamma'],
        "Delta":delta,
        "endcost":endcost,
        "PnL":PnL,
        "cptype":cptype,
        "interest2":interest2,
        "daily_pnl":daily_pnl
        }
# =============================================================================
#     if(np.abs(PnL)>2):
#         print("%d，%d的Pnl为%f"%(SecurityID,OptionID,PnL))
#         print(hedge_info[['Date','Delta','shares_bought','PnL']])
# =============================================================================
    return ret


stocks = stock_price['SecurityID'][~(stock_price['SecurityID'].duplicated())]

for stock in stocks:
    print("the SecurityID is :{}".format(stock))
    options = option_price[option_price['SecurityID']==stock]
    #创建文件夹
    file_path = "result/%d/"%stock
    if not os.path.exists(file_path):
        os.makedirs(file_path)
    
    for time2mat,t_options in options.groupby(['time2mat']):
        print("\t","the time2mat is {}".format(time2mat))
        # 如果time2mat小于7的话，就不管了，直接进入下一个循环
        if time2mat!=7:
            continue
        file_name = "%d_%d.json"%(stock,time2mat)
        days =  t_options['Date'][~(t_options['Date'].duplicated())]
        cusum_pnl =0
        for day in days:
            #print()
            td_options = t_options[t_options['Date']==day]
            
            # 去掉delta大于0.95和小于0.05的
            td_options = td_options[(np.abs(td_options['Delta'])<=0.95) & (np.abs(td_options['Delta'])>=0.05) ]
            # 去掉交易量为0的期权
            td_options = td_options[td_options['Volume']>0]
            optionIDs = td_options['OptionID'][~(td_options['OptionID'].duplicated())]
            
            pnl_cusum = 0
            n=0
            # 加入容错机制
            if len(optionIDs) == 0:
                continue
            
            for option in optionIDs: 
                
                #print("\t\t\t","the optionID is {}".format(option))
                end_date = pd.to_datetime(td_options['Expiration'].values[0])
                option_info = get_hedged_retDT1(stock,day,end_date,option)
                if option_info:
                    pnl_cusum = pnl_cusum+option_info['PnL']
                    n=n+1
                    with open(file_path+file_name,"a") as f:
                        f.write(json.dumps(option_info,cls=MyEncoder))
                        f.write("\n")
            cusum_pnl = cusum_pnl +pnl_cusum/n
            print("\t\t","the date is {}".format(day.strftime("%Y-%m-%d")),"average_PnL:{:.3f}".format(pnl_cusum/n),"Cusum_PnL:{:.3f} ".format(cusum_pnl))
#%%

# 读取数据并进行画图验证   109820 SPY  110015 XLU 112873 LWR  125558 XHB108105 SPX   
# SPY 重庆1603  XLU 易方达行业领先混合  
import jsonlines
import pandas as pd
data = []
code ='106445'  # 106544
with open('result/{}/{}_7.json'.format(code,code),'r') as f:
    for item in jsonlines.Reader(f):
        data.append(item)
    

data1 = pd.DataFrame(data)

#%%
import matplotlib.pyplot as plt
data1_ = data1[data1.cptype=="P"]
temp = data1_[['startdate','PnL']].groupby(['startdate']).mean()
temp.cumsum().plot()
plt.title("106445")

#%%
temp.plot()
#%%
import numpy as np

pnl_data =[]
for date,options in data1_.groupby(['startdate']):
    delta1 = -0.05
    delta2 = -0.5
    
    options.loc[:,'ddelta1'] = np.abs(options['Delta'] -  delta1)
    Strike1 = float(options['K'][options['ddelta1'] == min(options['ddelta1'])])
    
    options.loc[:,'ddelta2'] = np.abs(options['Delta'] -  delta2)
    Strike2 = float(options['K'][options['ddelta2'] == min(options['ddelta2'])])
    
    print(Strike1,Strike2)
    
    if (Strike1 > Strike2):
        temp = Strike1
        Strike1 = Strike2
        Strike2 = temp
        
    atm_options = options[(options['K']>=Strike1) & (options['K']<=Strike2)]
    pnl_data.append([date,atm_options['PnL'].mean()])
    

pnl_data = pd.DataFrame(pnl_data,columns=['date','PnL'])
pnl_data.set_index(['date'],inplace=True,drop=False)
pnl_data['date'] = pd.to_datetime(pnl_data['date'])
#pnl_data = pnl_data[pnl_data['date']>'2011-01-07']
pnl_data['PnL'].cumsum().plot()









